package archer.framework.core.lang.net;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class IpRange {

    private InetAddress mMinimumAddress;
    private InetAddress mMaximumAddress;
    private static Logger logger = LoggerFactory.getLogger(IpRange.class);

    /**
     * @param pAddress
     */
    public IpRange(InetAddress pAddress) {
        mMinimumAddress = pAddress;
        mMaximumAddress = pAddress;
    }

    /**
     * @param pMinimumAddress
     * @param pMaximumAddress
     */
    public IpRange(InetAddress pMinimumAddress, InetAddress pMaximumAddress) {
        if (IpRange.compare(pMinimumAddress, pMaximumAddress) == -1) {
            mMinimumAddress = pMinimumAddress;
            mMaximumAddress = pMaximumAddress;
        } else {
            mMinimumAddress = pMaximumAddress;
            mMaximumAddress = pMinimumAddress;
        }
    }

    /**
     * ddd.ddd.ddd.ddd - single IP address
     * ddd.ddd.ddd.* - class C range
     * ddd.ddd.* - class B range
     * ddd.* - class A range
     * ddd.ddd.ddd.ddd - ccc.ccc.ccc.ccc - specific range
     *
     * @param pPattern The pattern
     * @return The IPRange instance.
     */
    public static IpRange parse(String pPattern) {
        String oPattern = pPattern.replaceAll("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})\\.\\*", "$1.0-$1.255");
        oPattern = oPattern.replaceAll("([0-9]{1,3}\\.[0-9]{1,3})\\.\\*", "$1.0.0-$1.255.255");
        oPattern = oPattern.replaceAll("([0-9]{1,3})\\.\\*", "$1.0.0.0-$1.255.255.255");

        String[] oParts = oPattern.split("-");
        try {
            if (oParts.length > 1) {
                return new IpRange(InetAddress.getByName(oParts[0].trim()), InetAddress.getByName(oParts[1].trim()));
            } else {
                return new IpRange(InetAddress.getByName(oPattern.trim()));
            }
        } catch (UnknownHostException e) {
            logger.error("Error with pattern \"" + pPattern + "\".", e);
        }
        return null;
    }

    /**
     * Deteremines if the given IP address is in the range.
     *
     * @param pAddress
     * @return True if the address is in the range.
     */
    public boolean isInRange(InetAddress pAddress) {
        return IpRange.compare(getMinimumAddress(), pAddress) <= 0 && IpRange.compare(pAddress, getMaximumAddress()) <= 0;
    }

    /**
     * Compares two IPAddresses.
     * Less than zero {left} is less than {right}.
     * Zero {left} equals {right}.
     * Greater than zero {left} is greater than {right}.
     *
     * @param pLeft
     * @param pRight
     * @return
     */
    public static int compare(InetAddress pLeft, InetAddress pRight) {
        byte[] oLeftBytes = pLeft.getAddress();
        byte[] oRightBytes = pRight.getAddress();
        if (oLeftBytes.length != oRightBytes.length) {
            throw new IndexOutOfBoundsException();
        }
        int oLeft, oRight;
        // NB: bytes are signed so -1 is actually 255, and -64 is 192 for example.
        for (int i = 0; i < oLeftBytes.length; i++) {
            oLeft = oLeftBytes[i];
            if (oLeft < 0) oLeft = 256 + oLeft;
            oRight = oRightBytes[i];
            if (oRight < 0) oRight = 256 + oRight;
            //System.out.println(i + ": left byte = " + oLeftBytes[i] + " (" + oLeft +"), right byte = " + oRightBytes[i] + "(" + oRight + ")");
            if (oLeft < oRight) return -1;
            else if (oLeft > oRight) return 1;
        }
        return 0;
    }

    /**
     * @return Returns the maximumAddress.
     */
    public InetAddress getMaximumAddress() {
        return mMaximumAddress;
    }

    /**
     * @param pMaximumAddress The maximumAddress to set.
     */
    public void setMaximumAddress(InetAddress pMaximumAddress) {
        mMaximumAddress = pMaximumAddress;
    }

    /**
     * @return Returns the minimumAddress.
     */
    public InetAddress getMinimumAddress() {
        return mMinimumAddress;
    }

    /**
     * @param pMinimumAddress The minimumAddress to set.
     */
    public void setMinimumAddress(InetAddress pMinimumAddress) {
        mMinimumAddress = pMinimumAddress;
    }


}
